﻿using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using MoneyBook.Assets.Resources;
using MoneyBook.ComponentModel;
using MoneyBook.Web.Models;
using MoneyBook.Web.Services;
using System.ServiceModel.DomainServices.Client;
using System.Collections.Generic;
using System.Reactive.Subjects;
using System.Reactive.Linq;
using System.Collections.ObjectModel;
using MoneyBook.Reflection;
using MoneyBook.Web.Assets.Resources;

namespace MoneyBook.DialogViews {
    
    [EnhancePropertyAttributeSources(typeof(Account))]
    public partial class CreateNewAccountDialogModel : ViewModelBase {

        [CustomValidation(typeof(CreateNewAccountDialogModel), "ValidateNewAccountNameAlreadyExist")]
        [ViewModelProperty]
        private System.String accountName_;

        [ViewModelProperty]
        private System.String description_;

        public event EventHandler<ProcessCompletedEventArgs<Account>> CreateAccountCompleted;

        private AccountContext context = null;

        private List<String> existAccountNames = new List<String>();

        public CreateNewAccountDialogModel() {
            this.context = new AccountContext();
            this.PropertyChanged += CreateNewAccountDialogModel_PropertyChanged;
            this.context.PropertyChanged += context_PropertyChanged;
        }

        private void context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
            if (e.PropertyName == "IsLoading" || e.PropertyName == "IsSubmitting") {
                this.OnPropertyChanged("CanCreateAccount");
                this.OnPropertyChanged("IsProcessing");
            }
        }

        private void CreateNewAccountDialogModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
            if (e.PropertyName == "HasErrors" || e.PropertyName == "HasBindingValidationErrors") {
                this.OnPropertyChanged("CanCreateAccount");
            }
        }

        public void SetCurrentAccounts(ObservableCollection<Account> accounts) {
            existAccountNames.AddRange(accounts.Select(a => a.AccountName));
        }

        [ExcludeValidation]
        public bool CanCreateAccount {
            get {
                return !this.HasErrors && !this.context.IsLoading && !this.context.IsSubmitting;
            }
        }

        [ExcludeValidation]
        public bool IsProcessing {
            get {
                return this.context.IsLoading || this.context.IsSubmitting;
            }
        }

        public void CreateAccount() {
            if (!this.CanCreateAccount) {
                return;
            }

            this.ValidateAllProperties();

            if (!this.CanCreateAccount) {
                return;
            }

            context.Load(context.FindAccountQuery(WebContext.Current.User.MemberID, this.AccountName), op => {
                if(op.HasError){
                    this.AddError(op.Error.Message);
                    op.MarkErrorAsHandled();
                }else{
                    Account account = op.Entities.FirstOrDefault();
                    if (account != null) {
                        //TODO: resource
                        existAccountNames.Add(this.AccountName);
                        this.AddError("指定されたアカウント名は既に存在します。", "AccountName");
                    } else {
                        this.SubmitNewAccount();
                    }
                }
            }, null);
        }

        private void SubmitNewAccount() {
            Account account = new Account() {
                AccountName = this.AccountName,
                Description = this.Description,
                OwnerId = WebContext.Current.User.MemberID
            };

            context.Accounts.Add(account);
            
            context.SubmitChanges(op => {
                if (op.HasError) {
                    this.AddError(op.Error.Message);
                    op.MarkErrorAsHandled();
                } else {

                    GetAddedAccount(account);
                    
                }
                
            }, null);
        }

        private void GetAddedAccount(Account account) {
            context.Load(context.FindAccountQuery(WebContext.Current.User.MemberID, account.AccountName), op => {
                if (op.HasError) {
                    this.AddError(op.Error.Message);
                    op.MarkErrorAsHandled();
                } else {
                    Account result = op.Entities.FirstOrDefault();
                    //読み込み
                    result.Categories.ToArray();

                    context.Accounts.Detach(result);
                    if (this.CreateAccountCompleted != null) {
                        this.CreateAccountCompleted(this, new ProcessCompletedEventArgs<Account>(result));
                    }
                }
            }, null);
        }

        public static ValidationResult ValidateNewAccountNameAlreadyExist(string value, ValidationContext context) {
            CreateNewAccountDialogModel model = context.ObjectInstance as CreateNewAccountDialogModel;
            if (model == null) {
                //TODO: Exception Message;
                throw new InvalidOperationException();
            }
            if (model.existAccountNames == null) {
                return ValidationResult.Success;
            }
            if (string.IsNullOrEmpty(value)) {
                return ValidationResult.Success;
            }
            if (model.existAccountNames.Contains(value)) {
                //TODO: resource
                return new ValidationResult(string.Format("指定されたアカウント名は既に存在します。"));
            }
            return ValidationResult.Success;
        }

        protected override EnhancePropertyAttributeType GetEnhancePtopertyAttributeType() {
            return new EnhancePropertyAttributeType(typeof(CreateNewAccountDialogModel));
        }
    }
}
